#!/usr/bin/python
#coding=utf-8

import sys
import os
import rados
import threading
import commands
import time
import copy
import socket
import logging
import logging.config

import global_list
from command import *

class ReqUpdate:
    #self.h=链接集群的handle
    def __init__(self,handle,dict,reqid,logger):
        self.h=handle
        self.act=dict['act']
        self.host=dict['host']
        self.page=dict['page']
        self.pagesize=dict['pagesize']
        self.ver=dict['ver']
        self.reqid=int(reqid)
        self.logger=logger

    def ThreadDealer(self):
        resp={}
        resp['act']=str(self.act)
        resp['type']='update'
        resp['status']='running'
        if self.act == 'getstats':
            resp['page']=self.page

        #tname=threading.currentThread().getName()
        mtx=global_list.get_value('MUTEX')
        #加锁->获取结果列表->追加结果->更新列表->解锁
        mtx.acquire()
        res=global_list.get_value('RESULTS')
        res[int(self.reqid)]=copy.deepcopy(resp)
        global_list.set_value('RESULTS',res)
        mtx.release()

        #执行具体命令
        if self.act == 'getstats':
            ret=self.getstats()
            if False == ret.has_key('err'):
                resp.update(ret)
                resp['status']='success'
            else:
                resp['status']='failed'
        elif self.act == 'getdest':
            ret=self.getdest()
            if False == ret.has_key('err'):
                resp.update(ret)
                resp['status']='success'
            else:
                resp['status']='failed'
        elif self.act == 'doupdate':
            ret=self.doupdate()
            if False == ret.has_key('err'):
                resp.update(ret)
                resp['status']='success'
            else:
                resp['status']='failed'
        else:
            pass
 
        #更新结果
        mtx=global_list.get_value('MUTEX')
        mtx.acquire()
        res=global_list.get_value('RESULTS')
        res[int(self.reqid)]=copy.deepcopy(resp)
        global_list.set_value('RESULTS',res)
        mtx.release()

    #辅助函数，得到主机列表
    def gethosts(self):
        respdict=dict()

        cmd="/usr/bin/ceph osd tree | /usr/bin/grep -w host | /usr/bin/awk '{print $4}'"
        ret,hosts=commands.getstatusoutput(cmd)
        if ret != 0:
            self.logger.error("reqid:"+str(self.reqid)+" osd tree failed, ret="+str(ret))
            respdict['err']='ceph osd tree failed'
            return respdict
        lines=hosts.split('\n')
        respdict['hosts']=lines
        
        self.logger.info("reqid:"+str(self.reqid)+" success")
        return respdict

    def unset_norecovery(self):
        ret=os.system('/usr/bin/ceph osd unset norecover')
        if ret == 0:
            self.logger.info("reqid:"+str(self.reqid)+" unset norecover success")
            return True
        else:
            self.logger.error("reqid:"+str(self.reqid)+" unset norecover failed")
            return False

    def unset_nobackfill(self):
        ret=os.system('/usr/bin/ceph osd unset nobackfill')
        if ret == 0:
            self.logger.info("reqid:"+str(self.reqid)+" unset nobackfill success")
            return True
        else:
            self.logger.error("reqid:"+str(self.reqid)+" unset nobackfill failed")
            return False

    def unset_norebalance(self):
        ret=os.system('/usr/bin/ceph osd unset norebalance')
        if ret == 0:
            self.logger.info("reqid:"+str(self.reqid)+" unset norebalance success")
            return True
        else:
            self.logger.error("reqid:"+str(self.reqid)+" unset norebalance failed")
            return False

    def unset_noout(self):
        ret=os.system('/usr/bin/ceph osd unset noout')
        if ret == 0:
            self.logger.info("reqid:"+str(self.reqid)+" unset noout success")
            return True
        else:
            self.logger.error("reqid:"+str(self.reqid)+" unset noout failed")
            return False

    def set_norecovery(self):
        ret=os.system('/usr/bin/ceph osd set norecover')
        if ret == 0:
            self.logger.info("reqid:"+str(self.reqid)+" set norecover success")
            return True
        else:
            self.logger.error("reqid:"+str(self.reqid)+" set norecover failed")
            return False

    def set_nobackfill(self):
        ret=os.system('/usr/bin/ceph osd set nobackfill')
        if ret == 0:
            self.logger.info("reqid:"+str(self.reqid)+" set nobackfill success")
            return True
        else:
            self.logger.error("reqid:"+str(self.reqid)+" set nobackfill failed")
            return False

    def set_norebalance(self):
        ret=os.system('/usr/bin/ceph osd set norebalance')
        if ret == 0:
            self.logger.info("reqid:"+str(self.reqid)+" set norebalance success")
            return True
        else:
            self.logger.error("reqid:"+str(self.reqid)+" set norebalance failed")
            return False
   
    def set_noout(self):
        ret=os.system('/usr/bin/ceph osd set noout')
        if ret == 0:
            self.logger.info("reqid:"+str(self.reqid)+" set noout success")
            return True
        else:
            self.logger.error("reqid:"+str(self.reqid)+" set noout failed")
            return False

    #获取各个物理机当前操作状态
    def getstats(self):
        respdict=dict()
        resplist=[]

        #获取所有主机名
        ret=self.gethosts()
        if True == ret.has_key('err'):
            self.logger.error("reqid:"+str(self.reqid)+" get all hosts failed")
            respdict['err']=ret['err']
            return respdict

        #轮询查询各个主机的版本，状态
        idx=1
        for i in ret['hosts']:
            d=dict()
            d['id']=idx
            d['host']=str(i)
            d['ip']=socket.gethostbyname(str(i))
            #获取版本
            cmd="/usr/bin/ssh "+str(i)+" /usr/bin/ceph -v | /usr/bin/awk '{print $3}'"
            ret,res=commands.getstatusoutput(cmd)
            if ret != 0:
                self.logger.error("reqid:"+str(self.reqid)+" ret is "+str(ret)+" ,output is "+res)
                respdict['err']='yum info get ceph version failed'
                return respdict
            d['sourceversion']=res
            resplist.append(d)
            idx+=1
        respdict['list']=resplist

        self.logger.info("reqid:"+str(self.reqid)+" success")
        return respdict

    #获取升级目的版本
    def getdest(self):
        respdict=dict()

        #获取所有可升级版本
        cmd="/usr/bin/ssh "+str(self.host)+\
         " /usr/bin/yum info ceph 2>/dev/null | /usr/bin/grep ^Version | /usr/bin/awk -F: '{print $2}' | /usr/bin/sed 's/^[ \t]*//g'"
        ret,res=commands.getstatusoutput(cmd)
        if ret != 0:
            self.logger.error("reqid:"+str(self.reqid)+" ret is "+str(ret)+" ,output is "+res)
            respdict['err']='yum info get ceph version failed'
            return respdict

        destvers=res.split('\n')
        self.logger.info("reqid:"+str(self.reqid)+" get all ceph version: "+str(destvers))

        #与当前版本对比，排除小于或等于当前版本的数据
        #获取当前版本
        cmd="/usr/bin/ssh "+str(self.host)+" /usr/bin/ceph -v | /usr/bin/awk '{print $3}'"
        ret,soucvers=commands.getstatusoutput(cmd)
        if ret != 0:
            self.logger.error("reqid:"+str(self.reqid)+" ret is "+str(ret)+" ,output is "+res)
            respdict['err']='yum info get ceph version failed'
            return respdict
 
        #先比较大版本，再比较发行版本，最后比较小版本
        s1=int(soucvers.split('.')[0])
        s2=int(soucvers.split('.')[1])
        s3=int(soucvers.split('.')[2])
        tempvers=copy.deepcopy(destvers)
        for i in destvers:
            d1=int(i.split('.')[0])
            if d1 < s1:
                tempvers.remove(i)
                continue
            elif d1 > s1:
                continue
            else:
                d2=int(i.split('.')[1])
                if d2 < s2:
                    tempvers.remove(i)
                    continue
                elif d2 > s2:
                    continue
                else:
                    d3=int(i.split('.')[2])
                    if d3 <= s3:
                        tempvers.remove(i)
                        continue
                    else:
                        continue
        respdict['version']=tempvers

        self.logger.info("reqid:"+str(self.reqid)+" success")
        return respdict

    #辅助函数，检查集群状态是否为OK
    def get_healthOK(self):
        pg_stat = CephClusterCommand(self.h, prefix='pg stat', format='json')
        if pg_stat.has_key('err') == True:
            self.logger.error("reqid:"+str(self.reqid)+" pg stat failed, error is "+pg_stat['err'])
            return False
        result=pg_stat['result']
        num_pgs=result.get('num_pgs')
        state=result.get('num_pg_by_state')
        num_act_cln_pgs=0
        for i in state:
            if i['name'] == "active+clean":
                num_act_cln_pgs=i['num']
        if num_pgs == num_act_cln_pgs:
            self.logger.info("reqid:"+str(self.reqid)+" all PGs are active+clean")
            return True
        else:
            self.logger.error("reqid:"+str(self.reqid)+" not all PGs are active+clean")
            return False

    #执行升级操作
    def doupdate(self):
        respdict=dict()

        #利用yum升级ceph
        cmd="/usr/bin/ssh "+str(self.host)+" /usr/bin/yum -y update-to ceph-"+str(self.ver)
        ret=os.system(cmd)
        if ret != 0:
            self.logger.error("reqid:"+str(self.reqid)+" update-to "+str(self.ver)+" failed,ret is "+str(ret))
            respdict['err']='yum update-to ceph failed'
            return respdict

        #将ceph集群设置运维状态
        self.set_noout()
        self.set_norecovery()
        self.set_nobackfill()
        self.set_norebalance()

        #停止所有ceph进程
        cmd="/usr/bin/systemctl -H "+str(self.host)+" stop ceph.target"
        ret=os.system(cmd)
        if ret != 0:
            self.logger.error("reqid:"+str(self.reqid)+" stop ceph.target on "+str(self.host)+" failed,ret is"+str(ret))
            respdict['err']='systemctl stop ceph.target failed'
            return respdict

        #查询ceph状态
        cmd="/usr/bin/systemctl -H "+str(self.host)+" is-active ceph.target"
        ret,output=commands.getstatusoutput(cmd)
        if output != 'inactive':
            self.logger.error("reqid:"+str(self.reqid)+" ceph.target on "+str(self.host)+" are not inactive all,ret is "+str(ret))
            respdict['err']='ceph.target is still active or failed'
            return respdict

        #启动所有ceph进程
        cmd="/usr/bin/systemctl -H "+str(self.host)+" start ceph.target"
        ret=os.system(cmd)
        if ret != 0:
            self.logger.error("reqid:"+str(self.reqid)+" start ceph.target on "+str(self.host)+" failed,ret is"+str(ret))
            respdict['err']='systemctl start ceph.target failed'
            return respdict

        #查询ceph状态
        cmd="/usr/bin/systemctl -H "+str(self.host)+" is-active ceph.target"
        ret,output=commands.getstatusoutput(cmd)
        if output != 'active':
            self.logger.error("reqid:"+str(self.reqid)+" ceph.target on "+str(self.host)+" are not active all,ret is "+str(ret))
            respdict['err']='ceph.target is not active'
            return respdict

        self.unset_noout()
        self.unset_norecovery()
        self.unset_nobackfill()
        self.unset_norebalance()

        #等待集群回复正常
        while True:
            ret=self.get_healthOK()
            if ret == True:
                break
            else:
                time.sleep(5)
                continue

        #再次确认升级后版本
        cmd="/usr/bin/ssh "+str(self.host)+" /usr/bin/ceph -v | usr/bin/awk '{print $3}'"
        ret,now=commands.getstatusoutput(cmd)
        if ret != 0:
            self.logger.error("reqid:"+str(self.reqid)+" get ceph version failed, ret is "+str(ret))
            respdict['err']='ceph get version failed'
            return respdict
        respdict['current']=now

        self.logger.info("reqid:"+str(self.reqid)+" success")
        return respdict

    #析构
    def __del__(self):
        pass
